﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Linq;

namespace Xceed.Silverlight.Data
{
  internal class ReactiveDataPathEnumerator : ReactiveEnumerator<DataPath>
  {
    #region Constructor

    internal ReactiveDataPathEnumerator(
      IAsyncEnumerator<DataPath> childEnumerator,
      DataItemFactory<DataPath> dataItemFactory,
      INotifyDataSourceChanged dataSourceChanged,
      IContextHelper dataHelper,
      ThrottlingHandler throttlingHandler )
      : base( childEnumerator, dataItemFactory, dataSourceChanged, throttlingHandler )
    {
      if( dataHelper == null )
        throw new ArgumentNullException( "dataHelper" );

      m_dataHelper = dataHelper;
    }

    #endregion

    #region ShouldPerformFetchDelegate Internal Property

    internal Func<DataPath, bool> ShouldPerformFetchDelegate
    {
      get;
      set;
    }

    #endregion

    public void SetItemTemplateOverride( DataPath itemTemplate )
    {
      //A "null" itemTemplate is a valid "override". That
      //is why there is a "m_useItemTemplateOverride" bool flag
      //to hold the "validity" of the m_itemTemplateOverride value.
      m_itemTemplateOverride = itemTemplate;
      m_useItemTemplateOverride = true;
    }

    public void ClearItemTemplateOverride()
    {
      m_itemTemplateOverride = null;
      m_useItemTemplateOverride = false;
    }

    protected override DataPath CreateDummyItem( DataPath templateItem )
    {
      if( m_useItemTemplateOverride )
      {
        return base.CreateDummyItem( m_itemTemplateOverride );
      }
      else
      {
        return base.CreateDummyItem( templateItem );
      }
    }

    protected override bool ShouldPerformFetch( DataPath item )
    {
      // If the dummy for the item that is about to be fetched is no longer in the list
      // of dummies of the cache, it means that the item is no longer needed.
      return ( this.ShouldPerformFetchDelegate != null )
        ? this.ShouldPerformFetchDelegate( item )
        : base.ShouldPerformFetch( item );
    }

    protected override bool DummyDependOn( DataPath doesThisDummy, DataPath dependOnThisOne )
    {
      return ( doesThisDummy.IsAncestorOf( dependOnThisOne ) )
          || ( base.DummyDependOn( doesThisDummy, dependOnThisOne ) );
    }

    protected override DataPath CreateDummyTemplate( DataSourceContext context, SourcePosition sourcePosition )
    {
      DataPath datapath = base.CreateDummyTemplate( context, sourcePosition );

      if( datapath == null )
      {
        if( sourcePosition is SortPosition )
        {
          datapath = ReactiveDataPathEnumerator.CreatePathFromSortPosition( context.GroupContext, ( SortPosition )sourcePosition );

          datapath = datapath.ParentPath;
        }
      }
      return datapath;
    }

    protected override SourcePosition GetNewSourcePosition( DataSourceContext context, SourcePosition position )
    {
      if( context == null )
        throw new ArgumentNullException( "context" );

      ItemPosition itemPosition = position as ItemPosition;
      if( itemPosition != null )
        return ReactiveDataPathEnumerator.GetNewItemPosition( context.GroupContext, itemPosition );

      SortPosition sortPosition = position as SortPosition;
      if( sortPosition != null )
        return ReactiveDataPathEnumerator.GetNewSortPosition( context.GroupContext, sortPosition );

      return base.GetNewSourcePosition( context, position );
    }

    protected sealed override DataSourceContext GetCurrentContext()
    {
      return m_dataHelper.GetContext();
    }

    private static ItemPosition GetNewItemPosition( DataSourceGroupContext context, ItemPosition position )
    {
      if( position == null )
        throw new ArgumentNullException( "position" );

      if( context == null )
        throw new ArgumentNullException( "context" );

      if( !context.AnyCollapsedGroup )
        return position;

      DataPath item = position.Item as DataPath;
      if( item == null )
        throw new DataInternalException();

      int collapsedDepth = ReactiveDataPathEnumerator.GetCollapsedDepth( context, item );
      if( collapsedDepth <= 0 )
        return position;

      return new ItemPosition( item.CreateAncestorPath( collapsedDepth ) );
    }

    private static SortPosition GetNewSortPosition( DataSourceGroupContext context, SortPosition position )
    {
      if( position == null )
        throw new ArgumentNullException( "position" );

      if( context == null )
        throw new ArgumentNullException( "context" );

      if( !context.AnyCollapsedGroup )
        return position;

      DataPath groupPath = ReactiveDataPathEnumerator.CreatePathFromSortPosition( context, position );

      int collapsedDepth = ReactiveDataPathEnumerator.GetCollapsedDepth( context, groupPath );
      if( collapsedDepth <= 0 )
        return position;

      return new SortPosition(
        position.SortingReference,
        position.SortValues.Take( collapsedDepth ).ToList(),
        position.StartPosition );
    }

    private static int GetCollapsedDepth( DataSourceGroupContext context, DataPath path )
    {
      GroupLevelCollection groupCollection = context.GroupCollections;

      return groupCollection.GetCollapsedDepth( path );
    }

    private static DataPath CreatePathFromSortPosition(
      DataSourceGroupContext context,
      SortPosition sortPosition )
    {
      if( context == null )
        throw new ArgumentNullException( "context" );

      if( sortPosition == null )
        throw new ArgumentNullException( "sortPosition" );

      int sortValueCount = sortPosition.SortValues.Count;
      int pathDepth = Math.Min( sortValueCount, context.GroupDescriptions.Count );
      DataItem[] pathItems = new DataItem[ pathDepth ];
      for( int i = 0; i < pathDepth; i++ )
      {
        pathItems[ i ] = new GroupDataItem( sortPosition.SortValues[ i ] );
      }

      return new DataPath( pathItems );
    }

    #region Private Fields

    private readonly IContextHelper m_dataHelper;
    private DataPath m_itemTemplateOverride;
    private bool m_useItemTemplateOverride;

    #endregion
  }
}
